package com.chen.service.impl;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.exceptions.ApiException;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chen.config.IdAutoConfiguration;
import com.chen.domain.User;
import com.chen.domain.UserAuthAuditRecord;
import com.chen.domain.UserAuthInfo;
import com.chen.dto.AccountDto;
import com.chen.dto.UserDto;
import com.chen.enums.SMSEnum;
import com.chen.fegin.AccountFegin;
import com.chen.geetest.GeetestAutoConfiguration;
import com.chen.geetest.GeetestLib;
import com.chen.mapper.UserMapper;
import com.chen.model.GoogleVo;
import com.chen.model.UnsetPayPasswordVo;
import com.chen.model.UpdateLoginVo;
import com.chen.model.UpdateUserMobileVo;
import com.chen.service.UserAddressService;
import com.chen.service.UserAuthAuditRecordService;
import com.chen.service.UserAuthInfoService;
import com.chen.service.UserService;
import com.chen.utils.GoogleAuthenticator;
import com.chen.utils.UserUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.xml.bind.ValidationException;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private Snowflake snowflake = new Snowflake(1, 1);

    @Autowired
    private AccountFegin accountFegin;

    @Autowired
    private UserAddressService userAddressService;

    @Autowired
    private UserAuthAuditRecordService userAuthAuditRecordService;

    @Autowired
    private GeetestLib geetestLib;

    @Autowired
    private UserAuthInfoService userAuthInfoService;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private UserMapper userMapper;


    /**
     * 通过用户的Id 查询该用户邀请的人员
     *
     * @param page   分页参数
     * @param userId 用户的Id
     * @return
     */
    @Override
    public Page<User> findDirectInvitePage(Page<User> page, Long userId) {
        return page(page, new LambdaQueryWrapper<User>().eq(User::getDirectInviteid, userId));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void authsStatus(String id, String authStatus, String authCode, String remark) {
        User user = new User();
        user.setId(Long.parseLong(id));
        user.setReviewsStatus(Integer.parseInt(authStatus));
        if (authStatus.equals("1")) {
            user.setAuthStatus((byte) 2);
        }
        updateById(user);


        //获取到他上一次最后的审核数据 重新构造后添加历史记录
        UserAuthAuditRecord userAuthAuditRecord = new UserAuthAuditRecord();
        userAuthAuditRecord.setAuditUserId(0L);//todo  远程调用
        userAuthAuditRecord.setAuditUserName("");//todo  远程调用
        userAuthAuditRecord.setStep((byte) 2);
        userAuthAuditRecord.setStatus(Byte.valueOf(authStatus));
        userAuthAuditRecord.setRemark(remark);
        userAuthAuditRecord.setUserId(Long.parseLong(id));
        userAuthAuditRecord.setAuthCode(Long.parseLong(authCode));
        userAuthAuditRecord.setAuthCode(Long.parseLong(authStatus));
        userAuthAuditRecord.setId(snowflake.nextId());
        userAuthAuditRecordService.save(userAuthAuditRecord);

    }

    @Override
    public User getById(Serializable id) {
        User user = super.getById(id);

        if (user == null) {
            throw new IllegalArgumentException("请输入正确的用户Id");
        }
        Byte seniorAuthStatus = null; // 用户的高级认证状态
        String seniorAuthDesc = "";// 用户的高级认证未通过,原因
        Integer reviewsStatus = user.getReviewsStatus(); // 用户被审核的状态 1通过,2拒绝,0,待审核"
        if (reviewsStatus == -1) { //为-1 时,代表用户的资料没有上传
            seniorAuthStatus = 3;
            seniorAuthDesc = "资料未填写";
        } else {
            switch (reviewsStatus) {
                case 1:
                    seniorAuthStatus = 1;
                    seniorAuthDesc = "审核通过";
                    break;
                case 2:
                    seniorAuthStatus = 2;
                    // 查询被拒绝的原因--->审核记录里面的
                    // 时间排序,
                    List<UserAuthAuditRecord> userAuthAuditRecordList = userAuthAuditRecordService.getUserAuthAuditRecordList(user.getId());
                    if (!CollectionUtils.isEmpty(userAuthAuditRecordList)) {
                        UserAuthAuditRecord userAuthAuditRecord = userAuthAuditRecordList.get(0);
                        seniorAuthDesc = userAuthAuditRecord.getRemark();
                    } else {
                        seniorAuthDesc = "原因未知";
                    }
                    break;
                case 0:
                    seniorAuthStatus = 0;
                    seniorAuthDesc = "等待审核";
                    break;

            }
        }
        user.setSeniorAuthStatus(seniorAuthStatus);
        user.setSeniorAuthDesc(seniorAuthDesc);
        return user;
    }

    @Override
    public void authAccount(User user) throws ValidationException {
        //1.极验
        GeetestAutoConfiguration.checkGeetest(user, redisTemplate, geetestLib);

        //2.校验身份证
        Boolean check = IdAutoConfiguration.check(user.getIdCard(), user.getRealName());
        if (!check) {
            throw new ValidationException("身份校验失败！");
        }
        //3.更新数据
        String userId = SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString();
        user.setAuthStatus((byte) 1);
        user.setAuthtime(new Date());
        user.setId(Long.parseLong(userId));
        updateById(user);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void authUser(String[] imgas) {
        //1.改变user auth_status
        long userId = Long.parseLong(SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString());
        User user = getById(userId);
        user.setReviewsStatus(0);
        updateById(user);

        long authCode = snowflake.nextId();
        //2.添加数据到user_auth
        List<UserAuthInfo> userAuthInfos = new ArrayList<>();
        for (int i = 0; i < imgas.length; i++) {
            UserAuthInfo userAuthInfo = new UserAuthInfo();
            userAuthInfo.setImageUrl(imgas[i]);
            userAuthInfo.setSerialno(i);
            userAuthInfo.setAuthCode(authCode);
            userAuthInfo.setId(snowflake.nextId());
            userAuthInfo.setUserId(userId);
            userAuthInfo.setCreated(new Date());
            userAuthInfo.setLastUpdateTime(new Date());
            userAuthInfos.add(userAuthInfo);
        }
        userAuthInfoService.saveBatch(userAuthInfos);
    }

    @Override
    public void updatePhone(UpdateUserMobileVo updateUserMobileVo) {


        //验证旧手机验证码是否正确
        String userId = SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString();

        User user = getById(userId);

        String oldV = redisTemplate.opsForValue().get("SMS:VERIFY_OLD_PHONE:" + user.getMobile()).toString();

        if (StringUtils.isEmpty(oldV)) {
            throw new ApiException("验证码过期");
        }
        if (!oldV.equals(updateUserMobileVo.getOldValidateCode())) {
            throw new ApiException("验证码错误");
        }

        //验证新手机验证码是否正确

        String newV = redisTemplate.opsForValue().get("SMS:VERIFY_NEW_PHONE:" + updateUserMobileVo.getNewMobilePhone()).toString();

        if (StringUtils.isEmpty(newV)) {
            throw new ApiException("验证码过期");
        }
        if (!newV.equals(updateUserMobileVo.getValidateCode())) {
            throw new ApiException("验证码错误");
        }
        //修改用户手机号

        user.setCountryCode(updateUserMobileVo.getCountryCode());
        user.setMobile(updateUserMobileVo.getNewMobilePhone());
        user.setLastUpdateTime(new Date());
        //修改用户
        updateById(user);

    }

    @Override
    public void updateLoginPassword(UpdateLoginVo updateLoginVo) {
        String userId = SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString();
        User user = getById(userId);
        //校验旧密码是否正确
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        boolean matches = bCryptPasswordEncoder.matches(updateLoginVo.getOldpassword(), user.getPassword());
        if (!matches) {
            throw new IllegalArgumentException("用户的原始密码输入错误");
        }
        //校验手机验证码是否正确
        String validateCode = updateLoginVo.getValidateCode();
        String strV = (String) redisTemplate.opsForValue().get("SMS:" + SMSEnum.CHANGE_LOGIN_PWD_VERIFY + ":" + user.getMobile());

        if (!validateCode.equals(strV)) {
            throw new IllegalArgumentException("手机验证码错误");
        }
        //更新用户密码[注意加密哦]
        user.setPassword(bCryptPasswordEncoder.encode(updateLoginVo.getNewpassword()));
        updateById(user);
    }

    @Override
    public void updateUserPayPwd(UpdateLoginVo updateLoginVo) {
        String userId = SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString();
        User user = getById(userId);
        //校验旧交易密码是否正确
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        boolean matches = bCryptPasswordEncoder.matches(updateLoginVo.getOldpassword(), user.getPaypassword());
        if (!matches) {
            throw new IllegalArgumentException("用户的交易原始密码输入错误");
        }
        //校验手机验证码是否正确
        String validateCode = updateLoginVo.getValidateCode();
        String strV = (String) redisTemplate.opsForValue().get("SMS:CHANGE_PAY_PWD_VERIFY" + user.getMobile());

        if (!validateCode.equals(strV)) {
            throw new IllegalArgumentException("手机验证码错误");
        }
        //更新用户交易密码[注意加密哦]
        user.setPaypassword(bCryptPasswordEncoder.encode(updateLoginVo.getNewpassword()));
        updateById(user);
    }

    @Override
    public void unsetPayPassword(UnsetPayPasswordVo unsetPayPasswordVo) {
        //获取id
        String userId = SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString();

        User user = getById(userId);

        String validateCode = unsetPayPasswordVo.getValidateCode();
        String phoneValidate = (String) redisTemplate.opsForValue().get("SMS:FORGOT_PAY_PWD_VERIFY:" + user.getMobile());
        if (!validateCode.equals(phoneValidate)) {
            throw new IllegalArgumentException("用户的验证码错误");
        }
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        user.setPaypassword(bCryptPasswordEncoder.encode(unsetPayPasswordVo.getPayPassword()));

        updateById(user);
    }

    @Override
    public Map<Long, UserDto> getUser(List<Long> ids, String username, String mobile, Long pageX, Long size) {

        List<User> list;

        if (pageX != null && size != null) {
            Page<User> userPage = new Page<>(pageX, size);
            Page<User> page = page(userPage, new LambdaQueryWrapper<User>()
                    .in(!CollectionUtils.isEmpty(ids), User::getId, ids)
                    .eq(!StringUtils.isEmpty(username), User::getUsername, username)
                    .eq(!StringUtils.isEmpty(mobile), User::getMobile, mobile));
            list = page.getRecords();
        } else {
            list = list(new LambdaQueryWrapper<User>()
                    .in(!CollectionUtils.isEmpty(ids), User::getId, ids)
                    .like(!StringUtils.isEmpty(username), User::getUsername, username)
                    .like(!StringUtils.isEmpty(mobile), User::getMobile, mobile));
        }

        Map<Long, UserDto> result = new HashMap<>();
//        List<User> users = listByIds(ids);

        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyMap();
        }
//        List<UserDto> userDtos = UserDtoMapper.INSTANCE.convert2ListDto(users);
        List<UserDto> userDtos = list.stream().map(user -> {
            UserDto userDto = new UserDto();
            BeanUtils.copyProperties(user, userDto);
            return userDto;
        }).collect(Collectors.toList());
        for (int i = 0; i < userDtos.size(); i++) {
            result.put(userDtos.get(i).getId(), userDtos.get(i));
        }
        return result;
    }

    // member -> finance  ->  exchange -> finance -> member
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void register(User user) throws ValidationException {
        //极验
        GeetestAutoConfiguration.checkGeetest(user, redisTemplate, geetestLib);
        //校验手机号或者邮箱是否已被注册
        List<User> userList = userMapper.selectUserByMobileOrEmail(user);
        if (!CollectionUtils.isEmpty(userList)) {
            throw new ApiException("手机号或邮箱已被注册");
        }
        //邀请关系添加
        String inviteCode = user.getInviteCode();
        User one = null;
        if (!StringUtils.isEmpty(inviteCode)) {
            //查询邀请关系
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("invite_code", inviteCode);
            one = getOne(queryWrapper);
            if (one == null) {
                throw new ApiException("邀请码错误");
            }
            user.setInviteRelation(inviteCode);
            user.setDirectInviteid(one.getId().toString());
        }
        //注册用户【添加数据】
        String encodePwd = new BCryptPasswordEncoder().encode(user.getPassword());
        user.setPassword(encodePwd);
        user.setStatus((byte) 1);
        user.setType((byte) 1);
        user.setAuthStatus((byte) 0);
        user.setLogins(0);
        user.setId(snowflake.nextId());
        user.setReviewsStatus(-1);
        user.setInviteCode(RandomUtil.randomString(6)); // 用户的邀请码
        user.setCreated(new Date());
        user.setLastUpdateTime(new Date());

        //生成用户的账户信息  币种的钱包地址

        List<AccountDto> accountDtos = accountFegin.buildUserAccount(user.getId());
        if (CollectionUtils.isEmpty(accountDtos)) {
            throw new IllegalArgumentException("创建账户失败");
        }

        save(user);

    }

    @Override
    public void gaVerify(GoogleVo googleVo) {
        long t = System.currentTimeMillis();
        boolean b = GoogleAuthenticator.check_code(googleVo.getSecret(), Long.parseLong(googleVo.getCode()), t);
        if (!b) {
            throw new ApiException("谷歌验证码错误");
        }
        User user = getById(UserUtil.getUser());

        user.setGaSecret(googleVo.getSecret());
        user.setGaStatus(1);
        updateById(user);

    }

    @Override
    public void gaCancel(GoogleVo googleVo) {
        User user = getById(UserUtil.getUser());

        long t = System.currentTimeMillis();
        boolean b = GoogleAuthenticator.check_code(user.getGaSecret(), Long.parseLong(googleVo.getCode()), t);
        if (!b) {
            throw new ApiException("谷歌验证码错误");
        }
        user.setGaSecret(null);
        user.setGaStatus(0);
        updateById(user);
    }

}
